/*Copyright (c) 2009 Michael Streif
 *
 *Permission is hereby granted, free of charge, to any person obtaining a copy
 *of this software and associated documentation files (the "Software"), to deal
 *in the Software without restriction, including without limitation the rights
 *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *copies of the Software, and to permit persons to whom the Software is
 *furnished to do so, subject to the following conditions:
 *
 *The above copyright notice and this permission notice shall be included in
 *all copies or substantial portions of the Software.
 *
 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *THE SOFTWARE.
 */

#include "vmm.h"
#include "physmm.h"
#include "types.h"
#include "console.h"

#define PT_START	0xFFFFFF8000000000
#define PDP_START	0xFFFFFFFFFFE00000
#define PD_START    0xFFFFFFFFC0000000
#define PML4_START  0xFFFFFFFFFFFFF000

#define GET_PTE(vaddr) ((uint64*) (PT_START + ((((uint64) vaddr) >> 12LU) & 0x0000FFFFFFFFFFFFLU) * sizeof(uint64)))
#define GET_PDE(vaddr) ((uint64*) (PD_START + ((((uint64) vaddr) >> 21LU) & 0x0000FFFFFFFFFFFFLU) * sizeof(uint64)))
#define GET_PDPE(vaddr) ((uint64*) (PDP_START + ((((uint64) vaddr) >> 30LU) & 0x0000FFFFFFFFFFFFLU) * sizeof(uint64)))
#define GET_PML4E(vaddr) ((uint64*) (PML4_START + ((((uint64) vaddr) >> 49LU) & 0x0000FFFFFFFFFFFFLU) * sizeof(uint64)))

extern void reloadCR3();


// TODO: Don't know if I have to flush the TLB everytime i update any paging struct!!!
void mapPage(uint64* vaddr, uint16 flags)
{
	uint64* PTE = GET_PTE(vaddr);
	uint64* PDE = GET_PDE(vaddr);
	uint64* PDPE = GET_PDPE(vaddr);
	uint64* PML4E = GET_PML4E(vaddr);

	uint64* physPage;

	if (*PML4E == 0)
	{
		// PML4E
		physPage = AllocPageFrame();

		*PML4E = (unsigned long) physPage | flags;

		// PDPE
		physPage = AllocPageFrame();

		*PDPE = (unsigned long) physPage | flags;

		// PDE
		physPage = AllocPageFrame();

		*PDE = (unsigned long) physPage | flags;
	}
	else if (*PDPE == 0)
	{
		// PDPE
		physPage = AllocPageFrame();

		*PDPE = (unsigned long) physPage | flags;

		// PDE
		physPage = AllocPageFrame();

		*PDE = (unsigned long) physPage | flags;
	}
	else if (*PDE == 0)
	{
		// PDE
		physPage = AllocPageFrame();

		*PDE = (unsigned long) physPage | flags;
	}

	// PTE
	physPage = AllocPageFrame();

	*PTE = (unsigned long) physPage | flags;

	reloadCR3();

}

void unmapPage(uint64* vaddr, char free)
{
	uint64* PTE = GET_PTE(vaddr);

	if (*PTE != 0)
	{
		if (free)
		{
			FreePageFrame((uint64*) (*PTE & 0xFFFFFFFFFFFFF000));
		}

		*PTE = 0;

		reloadCR3();
	}
}
